<html>
	<head>
		<title>Final Project</title>
		<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
		<script type="text/javascript" src="Scripts/glMatrix-0.9.5.min.js"></script>
		<script type="text/javascript" src="Scripts/webgl-utils.js"></script>
		<script type="text/javascript" src="Scripts/loaders.js"></script>
		
		<script id="uv-fs" type="x-shader/x-fragment">
			#ifdef GL_FRAGMENT_PRECISION_HIGH
			precision highp float;
			#else
			precision mediump float;
			#endif
			
			uniform sampler2D texture0; //texture map
			
			varying vec2 vTexCoord;
			
			void main(void) 
			{
				gl_FragColor = texture2D(texture0, vTexCoord);
			}
		</script>
		<script id="uv-vs" type="x-shader/x-vertex">
			uniform mat4 pMatrix;
			uniform mat4 mvMatrix;


			attribute vec3 a_position;
			attribute vec2 a_texCoord;
			
			varying vec2 vTexCoord;
			
			void main(void) {
				vTexCoord = a_texCoord;
				gl_Position = pMatrix * mvMatrix * vec4(a_position, 1.0);	
			}
			
		</script>
		
		<script id="fur-fs" type="x-shader/x-fragment">
			#ifdef GL_FRAGMENT_PRECISION_HIGH
			precision highp float;
			#else
			precision mediump float;
			#endif

			uniform sampler2D texture0; //noise
			uniform sampler2D texture1; //texture map

			uniform float dampen;
			
			varying vec2 vTexCoord;

			void main(void)
			{
			// Get the alpha component for this shaft
			vec2 tex = vTexCoord.xy;
			vec4 fAlpha = texture2D( texture0, tex*6.0)*dampen;
	
			// Get the hair shaft color, and scale by the color scale
			vec4 fColor = texture2D( texture1, tex ) * fAlpha;

			// Return the calculated color
			gl_FragColor = fColor;// + fAlpha;
			}
		</script>
		<script id="fur-vs" type="x-shader/x-vertex">
			uniform mat4 pMatrix;
			uniform mat4 mvMatrix;
			uniform float shell_distance;
			uniform float pass_index;

			attribute vec3 a_position;
			attribute vec3 a_normal;
			attribute vec2 a_texCoord;

			varying vec2  vTexCoord;

			void main(void)
			{
			vTexCoord    = a_texCoord.xy;

			vec4 Position = vec4(a_position,1.0);
			Position.xyz += shell_distance * pass_index * a_normal;
			gl_Position = pMatrix * mvMatrix * Position;
			}
		</script>
		
		<script type="text/javascript">
			/*templates are used in order to make intellisense work...helps with debugging and typos*/
			var gl , canvas;
			var pMatrix = mat4.create(), mvMatrix;
			var buffer_template = function() {
				var object = {};
				object.itemSize = 0;
				object.numItems = 0;
				return object;
			}
			
		
			
			var fur_template = function(filename, texture_files, passes, dampen, shellDistance) {
				var object = {};
				object.filename = filename;
				object.loaded = false;
				object.textures = [];
				object.texture_files = texture_files;
				object.positionBuffer = buffer_template();
				object.indexBuffer = buffer_template();
				object.normalBuffer = buffer_template();
				object.texBuffer = buffer_template();
				object.mvMatrix = mat4.create();
				object.passes = passes;
				object.dampen = dampen;
				object.shellDistance = shellDistance;
				return object;
			}
			
			var furObj = [fur_template("tiger.json", ["furNoise.png", "tiger.jpg"],15,0.75,0.001)];
			var all_textures = [];

			var shader_template = function(numTextures) {
				var object = {};
				object.program = {};
				object.vertexPositionAttribute = {};
				object.vertexNormalAttribute = {};
				object.vertexTexCoordAttribute = {};
				object.pMatrixUniform = {};
				object.mvMatrixUniform = {};
				object.textureUniforms = new Array(numTextures);
				return object;
			}
			var furShader = shader_template(2);
			/*additional uniforms unique to fur shader*/
			furShader.passIndexUniform = {};
			furShader.shellDistanceUniform = {};
			furShader.dampenUniform = {};
			
			var uvShader = shader_template(1);

			function initGL() {
				try {
					
					gl = canvas.getContext("experimental-webgl");
					resize();
					
				} catch (e) {
				}
				if(!gl) {
					alert("Could not initialise WebGL, sorry :-(");
				}
			}

			function getShader(gl, id) {
				var shaderScript = document.getElementById(id);
				if(!shaderScript) {
					return null;
				}

				var str = "";
				var k = shaderScript.firstChild;
				while(k) {
					if(k.nodeType == 3) {
						str += k.textContent;
					}
					k = k.nextSibling;
				}

				var shader;
				if(shaderScript.type == "x-shader/x-fragment") {
					shader = gl.createShader(gl.FRAGMENT_SHADER);
				} else if(shaderScript.type == "x-shader/x-vertex") {
					shader = gl.createShader(gl.VERTEX_SHADER);
				} else {
					return null;
				}

				gl.shaderSource(shader, str);
				gl.compileShader(shader);

				if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
					alert(gl.getShaderInfoLog(shader));
					return null;
				}

				return shader;
			}

			var shaderProgram;

			function initShaders() {
				
				
				fragmentShader = getShader(gl, "fur-fs");
				vertexShader = getShader(gl, "fur-vs");
				furShader.program = gl.createProgram();
				gl.attachShader(furShader.program, vertexShader);
				gl.attachShader(furShader.program, fragmentShader);
				gl.linkProgram(furShader.program);
				
				if (!gl.getProgramParameter(furShader.program, gl.LINK_STATUS)) {
					alert("Could not initialize shaders");
				}
				
				gl.useProgram(furShader.program);
				
				furShader.vertexPositionAttribute = gl.getAttribLocation(furShader.program, "a_position");
				gl.enableVertexAttribArray(furShader.vertexPositionAttribute);
				furShader.vertexNormalAttribute = gl.getAttribLocation(furShader.program, "a_normal");
				gl.enableVertexAttribArray(furShader.vertexNormalAttribute);
				furShader.vertexTexCoordAttribute = gl.getAttribLocation(furShader.program, "a_texCoord");
				gl.enableVertexAttribArray(furShader.vertexTexCoordAttribute);
				
				furShader.pMatrixUniform = gl.getUniformLocation(furShader.program, "pMatrix");
				furShader.mvMatrixUniform = gl.getUniformLocation(furShader.program, "mvMatrix");
				furShader.passIndexUniform = gl.getUniformLocation(furShader.program, "pass_index");
				furShader.shellDistanceUniform = gl.getUniformLocation(furShader.program, "shell_distance");
				furShader.dampenUniform = gl.getUniformLocation(furShader.program, "dampen");
				for (var i=0;i<furShader.textureUniforms.length;i++) {
					furShader.textureUniforms[i] = gl.getUniformLocation(furShader.program, "texture"+i);
					
				}
		
				

				fragmentShader = getShader(gl, "uv-fs");
				vertexShader = getShader(gl, "uv-vs");
				uvShader.program = gl.createProgram();
			
				gl.attachShader(uvShader.program, vertexShader);
				gl.attachShader(uvShader.program, fragmentShader);
				gl.linkProgram(uvShader.program);
			
				if (!gl.getProgramParameter(uvShader.program, gl.LINK_STATUS)) {
					alert("Could not initialize shaders");
				}
			
				gl.useProgram(uvShader.program);
				
				uvShader.vertexPositionAttribute = gl.getAttribLocation(uvShader.program, "a_position");
				gl.enableVertexAttribArray(uvShader.vertexPositionAttribute);
				uvShader.vertexTexCoordAttribute = gl.getAttribLocation(uvShader.program, "a_texCoord");
				gl.enableVertexAttribArray(uvShader.vertexTexCoordAttribute);
				
				uvShader.pMatrixUniform = gl.getUniformLocation(uvShader.program, "pMatrix");
				uvShader.mvMatrixUniform = gl.getUniformLocation(uvShader.program, "mvMatrix");
				
				for (var i=0;i<uvShader.textureUniforms.length;i++) {
					uvShader.textureUniforms[i] = gl.getUniformLocation(uvShader.program, "texture"+i);
				}
			
			}

			
			
			 var mvMatrixStack = [];
			
			 function mvPushMatrix() {
			 var copy = mat4.create();
			 mat4.set(mvMatrix, copy);
			 mvMatrixStack.push(copy);
			 }
			
			 function mvPopMatrix() {
			 if (mvMatrixStack.length == 0) {
			 throw "Invalid popMatrix!";
			 }
			 mvMatrix = mvMatrixStack.pop();
			 }

	
			 

			function degToRad(degrees) {
				return degrees * Math.PI / 180;
				
			}

			
			function drawScene() {
				gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
				gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

				if (furObj[0].loaded == false) return;
				
			
				mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
				mat4.translate(pMatrix, [0, 0, -1]);
				
				
				
				
				//render furry objects
				for (var i=0;i<furObj.length;i++) {
					var posBuffer = furObj[i].positionBuffer;
					var texBuffer = furObj[i].texBuffer;
					var normBuffer = furObj[i].normalBuffer;
					var indBuffer = furObj[i].indexBuffer;
					
					mvMatrix = furObj[i].mvMatrix;
					mat4.identity(mvMatrix);
					mat4.rotate(mvMatrix, degToRad(objectAngle),[0,1,0]);
					
					mat4.scale(mvMatrix, [0.75, 0.75, 0.75]);
					
					gl.activeTexture(gl.TEXTURE0);
					gl.bindTexture(gl.TEXTURE_2D, furObj[i].textures[0]);
					gl.activeTexture(gl.TEXTURE1);
					gl.bindTexture(gl.TEXTURE_2D, furObj[i].textures[1]);
					
					/*initial pass (skin), just do uv texture*/
					gl.disable(gl.BLEND);
					gl.useProgram(uvShader.program);
					
					gl.uniform1i(uvShader.textureUniforms[0],1);
					gl.uniformMatrix4fv(uvShader.pMatrixUniform, false, pMatrix);
					gl.uniformMatrix4fv(uvShader.mvMatrixUniform, false, mvMatrix);
					
					gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
					gl.vertexAttribPointer(uvShader.vertexPositionAttribute, posBuffer.itemSize, gl.FLOAT, false ,0, 0);
					gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer);
					gl.vertexAttribPointer(uvShader.vertexTexCoordAttribute, texBuffer.itemSize, gl.FLOAT, false, 0, 0);
					gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuffer);
					
					gl.drawElements(gl.TRIANGLES, indBuffer.numItems, gl.UNSIGNED_SHORT,0);
					
					/*render fur shells*/
					gl.enable(gl.BLEND);
					gl.useProgram(furShader.program);
					
					gl.uniform1i(furShader.textureUniforms[0], 0);
					gl.uniform1i(furShader.textureUniforms[1], 1);
					gl.uniformMatrix4fv(furShader.pMatrixUniform, false, pMatrix);
					gl.uniformMatrix4fv(furShader.mvMatrixUniform, false, mvMatrix);
					gl.uniform1f(furShader.dampenUniform, furObj[i].dampen);
					gl.uniform1f(furShader.shellDistanceUniform, furObj[i].shellDistance);
					
					gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
					gl.vertexAttribPointer(furShader.vertexPositionAttribute, posBuffer.itemSize, gl.FLOAT, false, 0, 0);
					gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer);
					gl.vertexAttribPointer(furShader.vertexTexCoordAttribute, texBuffer.itemSize, gl.FLOAT, false, 0, 0);
					gl.bindBuffer(gl.ARRAY_BUFFER, normBuffer);
					gl.vertexAttribPointer(furShader.vertexNormalAttribute, normBuffer.itemSize, gl.FLOAT, false, 0, 0);
		
					
					for (var j=1;j<furObj[i].passes;j++) {
						gl.uniform1f(furShader.passIndexUniform, j);
						gl.drawElements(gl.TRIANGLES, indBuffer.numItems, gl.UNSIGNED_SHORT,0);
					}
				}
		
			}
			
			function webGLStart() {
				canvas = document.getElementById("webgl-canvas");
				initGL(canvas);
				initShaders();
				//initTextures();
				loadObjects();

				gl.clearColor(0.0, 0.0, 0.0, 1.0);
				gl.enable(gl.DEPTH_TEST);
				
				gl.blendEquation(gl.ADD);
				//gl.blendFunc(gl.SRC_COLOR, gl.DST_ALPHA); -- leave this here..alternate blending function..need to test them some more to find the best one
				gl.blendFuncSeparate(gl.SRC_COLOR, gl.DST_ALPHA, gl.SRC_COLOR, gl.DST_ALPHA); //was src_alpha, one, one_minus_dst_alpha, dst_alpha
				tick();
			}
			
			
			var lastTime = 0;
			var objectAngle = 180;

			function animate() {
				var timeNow = new Date().getTime();
				if(lastTime != 0) {
					var elapsed = timeNow - lastTime;
					objectAngle += 0.05 * elapsed;

				}
				lastTime = timeNow;
			}

			function tick() {
				requestAnimFrame(tick);
				drawScene();
				animate();
			}
			
			function resize() {
				gl.viewportWidth = canvas.width = window.innerWidth;
				gl.viewportHeight = canvas.height = window.innerHeight;
			}
			
		</script>
		<style type="text/css">
			#loadingtext {
				position: absolute;
				top: 250px;
				left: 150px;
				font-size: 2em;
				color: white;
			}
		</style>
	</head>
	<body onload="webGLStart();" onresize="resize()">
		<canvas id="webgl-canvas" style="border: none;" width="1024" height="768"></canvas>
		<div id="loadingtext">
			Loading world...
		</div>
		<br/>
	</body>
</html>
